package express.check;

import java.util.ArrayList;
import java.util.List;

/**
 * 表达式校验
 *
 * @author miweidong
 */
public class ExpressCheck {
    /**
     * 下标索引
     */
    private Integer idx = 0;

    /**
     * 退出标识符
     */
    private Integer sym = 0;

    /**
     * 错误码
     */
    private Integer err = 0;

    /**
     * 单词集合
     */
    private List<Words> words = new ArrayList<>();

    /**
     * 词法分析
     *
     * @param express 数学表达式
     * @return 词法校验结果
     */
    public Integer workAnalysis(String express) {
        char min = '0';
        char max = '9';
        for (int i = 0; i < express.length(); i++) {
            char word = express.charAt(i);
            //判断单词合法性
            if (word == '(' || word == ')' || word == '+' || word == '-' ||
                    word == '*' || word == '/') {
                switch (word) {
                    case '+':
                        this.words.add(new Words(String.valueOf(word), 1));
                        break;
                    case '-':
                        this.words.add(new Words(String.valueOf(word), 2));
                        break;
                    case '*':
                        this.words.add(new Words(String.valueOf(word), 3));
                        break;
                    case '/':
                        this.words.add(new Words(String.valueOf(word), 4));
                        break;
                    case '(':
                        this.words.add(new Words(String.valueOf(word), 6));
                        break;
                    case ')':
                        this.words.add(new Words(String.valueOf(word), 7));
                        break;
                    default:
                }
            } else if (word >= min && word <= max) {
                StringBuilder temp = new StringBuilder();
                while (express.charAt(i) >= min && express.charAt(i) <= max) {
                    temp.append(express.charAt(i));
                    if (i >= express.length() - 1) {
                        this.words.add(new Words(temp.toString(), 5));
                        return 1;
                    }
                    ++i;
                }
                if (express.charAt(i) == '.') {
                    temp.append(express.charAt(i));
                    ++i;
                    if (express.charAt(i) >= min && express.charAt(i) <= max) {
                        while (express.charAt(i) >= min && express.charAt(i) <= max) {
                            temp.append(express.charAt(i));
                            if (i >= express.length() - 1) {
                                this.words.add(new Words(temp.toString(), 5));
                                return 1;
                            }
                            ++i;
                        }
                    } else {
                        return -1;
                    }
                }
                this.words.add(new Words(temp.toString(), 5));
                --i;
            } else {
                return -1;
            }
        }
        return 0;
    }

    /**
     * 语法分析
     * 读取下一个单词的种别比编码
     */
    public void Next() {
        if (this.idx < this.words.size()) {
            this.sym = words.get(this.idx++).getSymbol();
        } else {
            sym = 0;
        }
    }

    /**
     * E -> TE`
     */
    public void E() {
        T();
        E1();
    }

    /**
     * E` -> +TE` | -TE`| ε
     */
    public void E1() {
        if (this.sym == 1) {
            Next();
            T();
            E1();
        } else if (this.sym == 2) {
            Next();
            T();
            E1();
        } else if (this.sym != 7 && this.sym != 0) {
            err = -1;
        }
    }

    /**
     * T -> FT`
     */
    public void T() {
        F();
        T1();
    }

    /**
     * T` -> *FT` | /FT` | ε
     */
    public void T1() {
        if (this.sym == 3) {
            Next();
            F();
            T1();
        } else if (this.sym == 4) {
            Next();
            F();
            T1();
        } else if (this.sym != 1 && this.sym != 2 && this.sym != 7 && this.sym != 0) {
            this.err = -1;
        }
    }

    /**
     * F -> (E) | d
     */
    public void F() {
        if (this.sym == 5) {
            Next();
        } else if (this.sym == 6) {
            Next();
            E();
            if (this.sym == 7) {
                Next();
            } else {
                this.err = -1;
            }
        } else {
            this.err = -1;
        }
    }

    public List<Words> getWords() {
        return words;
    }

    public Integer getIdx() {
        return idx;
    }

    public Integer getSym() {
        return sym;
    }

    public Integer getErr() {
        return err;
    }
}
